package com.improve.frame.redis.lock;

import com.improve.frame.common.constant.CommonConstant;
import com.improve.frame.common.exception.LockException;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.util.Assert;

import java.util.concurrent.TimeUnit;

/**
 * @Description Redisson分布式锁实现
 * @Author wzxing918
 * @Date 2021-03-31
 * @Version 1.0.0
 **/
@ConditionalOnClass(RedissonClient.class)
@ConditionalOnProperty(prefix = "improve.lock", name = "lockType", havingValue = "redis", matchIfMissing = true)
public class RedissonDistributedLock implements DistributedLock {
    @Autowired
    private RedissonClient redisson;

    private WzxLock getLock(String key, boolean isFair) {
        Assert.hasLength(key, "key can not be empty");
        RLock lock;
        if (isFair) {
            lock = redisson.getFairLock(CommonConstant.LOCK_KEY_PREFIX + key);
        } else {
            lock =  redisson.getLock(CommonConstant.LOCK_KEY_PREFIX + key);
        }
        return new WzxLock(lock, this);
    }

    @Override
    public WzxLock lock(String key, long leaseTime, TimeUnit unit, boolean isFair) {
        Assert.hasLength(key, "key can not be empty");
        WzxLock wzxLock = getLock(key, isFair);
        RLock lock = (RLock)wzxLock.getLock();
        lock.lock(leaseTime, unit);
        return wzxLock;
    }

    @Override
    public WzxLock tryLock(String key, long waitTime, long leaseTime, TimeUnit unit, boolean isFair) throws InterruptedException {
        Assert.hasLength(key, "key can not be empty");
        WzxLock wzxLock = getLock(key, isFair);
        RLock lock = (RLock)wzxLock.getLock();
        if (lock.tryLock(waitTime, leaseTime, unit)) {
            return wzxLock;
        }
        return null;
    }

    @Override
    public void unlock(Object lock) {
        Assert.notNull(lock, "lock can not be null");

        if (lock instanceof RLock) {
            RLock rLock = (RLock)lock;
            if (rLock.isLocked()) {
                rLock.unlock();
            }
        } else {
            throw new LockException("requires RLock type");
        }
    }
}